#!/usr/bin/python
# encoding: utf-8

from datetime import datetime

class action:
  def __init__ (self, name, types, doc, forPython=True):
    def uncapitalize (text):
      return text[0].lower() + text[1:]
    self.info = {'name': name, 'doc': doc, 'pyname': uncapitalize(name), 'forPython': forPython}
    self.pyvars = [{'type':t, 'var':v, 'pyvar': uncapitalize(v)} for (t,v) in types]
    self.vars = [{'type': 'IMainForm', 'var': 'Form'}] + self.pyvars

actions = \
  [action('SetCommandMode', [], 'change mode of main form into command mode.'),
   action('SetSelectMode', [], 'change mode of main form into select mode.'),
   action('NewFileList', [('PWideChar','PathName'),('PWideChar','Command'),('Integer','FileCount')],
          'reset file list.', False),
   action('AddFile', [('PWideChar','FileName'),('Integer','TextletCount')],
          'add file name to file list.', False),
   action('AddFileTextlet', [('Integer','Level'),('Integer','Left'),('Integer','Width'),
                             ('Integer','Font'),('PWideChar','Text')],
          'add file textlet to file.', False),
   action('CommitFileList', [], 'commit file list changes and update main form.', False),
   action('NewSkin', [('Integer','BgColor'),('Integer','SelectedColor'),
                      ('Integer','ItemHeightsCount'),('Integer','FontsCount')],
          'reset skin', False),
   action('AddItemHeight', [('Integer','Height')], 'add item height to skin', False),
   action('AddFont', [('PWideChar','Name'),('Integer','Size'),('PWideChar','Style'),
                      ('PWideChar','Pitch'),('Integer','Color'),('Integer','Charset')],
          'add font to skin', False),
   action('CommitSkin', [], 'commit skin changes', False),
   action('Close', [], 'close main form.'),
   action('Up', [('Integer','Step')], 'move file list focus up by "step".'),
   action('Down', [('Integer','Step')], 'move file list focus down by "step".'),
   action('ToggleSelection', [('Integer','Index')], 'toggle file selection at "index".'),
   action('SelectAll', [], 'select all files.'),
   action('UnselectAll', [], 'unselect all files.'),
   action('ContextMenu', [], 'popup context menu for selected files'),
   action('DragDropContextMenu', [], 'popup dragdrop context menu for dropped files'),
   action('DragStart', [], 'start dragging'),
   action('SetDragDropEffect', [('Integer','Effect')], 'set dragdrop effect')
  ]

#
# codegen for delphi
#
def delphi_decl (action):
  return "function %(name)s(Self, Args: PPyObject): PPyObject; cdecl;" \
    % action.info

def delphi_impl (action):
  def typeToChar (type):
    if 'PWideChar' == type:
      return 'u'
    else:
      return 'i'
  def castIfString (var, type):
    if 'PWideChar' == type:
      return 'WideString(' + var + ')'
    else:
      return var

  decl = '\n    '.join(['%(var)s: %(type)s;' % v for v in action.vars])
  type = ''.join([typeToChar(v['type']) for v in action.vars])
  recv = ', '.join(['@%(var)s' % v for v in action.vars])
  args = ', '.join([castIfString(v['var'], v['type']) % v for v in action.vars])

  return """
function %(name)s(Self, Args: PPyObject): PPyObject; cdecl;
  var
    %(varsdecl)s
  begin
    if fromArgs(Args, '%(varstype)s', [%(varsrecv)s], Result) then begin
      Actions.%(name)s(%(varsargs)s);
      Result := nul;
    end;
  end;
""" % dict(action.info, varsdecl=decl, varstype=type, varsrecv=recv, varsargs=args)

def delphi_recs (action):
  return "(name: '%(pyname)s'; func: %(name)s; flg: METH_VARARGS; doc: '%(doc)s')" \
    % action.info

def delphi_unit (f):
  def gen ():
    yield '''(*
 * This is an auto-generated file by actionsgen.py at %(TIME)s.
 *)
unit PyActions;

interface

uses Python;
''' % {'TIME': datetime.now().isoformat()}

    for a in actions:
      yield delphi_decl(a) + '\n'

    yield '''
const
  METHODS: array[0..%(len)d] of PyMethodDef =
  (''' % {'len': len(actions)}

    for a in actions:
      yield delphi_recs(a) + ',\n   '

    yield '''(name: nil; func: nil; flg: 0; doc: nil));

implementation

uses Actions, Models;
'''

    for a in actions:
      yield delphi_impl(a)

    yield '''
end.
'''

  for buf in gen():
    f.write(buf.decode('utf-8').encode('cp932'))

#
# codegen for python
#
def python_method (action):
  def castIfString (var, type):
    if 'PWideChar' == type:
      return 'unicode(' + var + ')'
    else:
      return var
  if action.pyvars:
    pargs = 'self, ' + ', '.join([v['pyvar'] for v in action.pyvars])
    dargs = 'self._mainform, ' + ', '.join([castIfString(v['pyvar'], v['type']) for v in action.pyvars])
  else:
    pargs = 'self'
    dargs = 'self._mainform'

  return '''
  def %(pyname)s (%(pargs)s):
    '%(doc)s'
    res = _actions.%(pyname)s(%(dargs)s)
    if res: raise LishActionError, res
''' % dict(action.info, pargs=pargs, dargs=dargs)

def python_module (f):
  def gen():
    yield '''#!/usr/bin/python
# encoding: utf-8

import _actions

class LishActionError(Exception):
  "Raised when a call for delphi module was failed."
  pass

class Actions:
  def __init__ (self, mainform):
    self._mainform = mainform
'''

    for a in filter(lambda (a): a.info['forPython'], actions):
      yield python_method(a)

  for buf in gen():
    f.write(buf)
